package com.crazedcoders.lib.structure
{
	/**
	 *Simple CRUD Code Generator
	 *Email:  support@crazedcoders.com
	 *Website: http://www.crazedcoders.com/
	 *Copyright (C) 2007 CrazedCoders
	 *
	 *This program is free software: you can redistribute it and/or modify
	 *it under the terms of the GNU General Public License as published by
	 *the Free Software Foundation, either version 3 of the License, or
	 *(at your option) any later version.
	 *
	 *THE PROGRAM IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	 *SOFTWARE.
	 *
	 *You should have received a copy of the GNU General Public License
	 *along with this program.  If not, see <http://www.gnu.org/licenses/>.
	 */
	
	import mx.collections.ArrayCollection;
	import mx.events.CollectionEvent;
	import mx.events.CollectionEventKind;
	import mx.events.PropertyChangeEventKind;
	import mx.events.PropertyChangeEvent;
	
	public class TypedArrayCollection extends ArrayCollection 
	{
		protected var type:Class = null;
		function TypedArrayCollection( type:Class ) {
			this.type = type;
		}
		
		override public function addItem(item:Object):void {
			if( item is type ) {
				super.addItem( item );
			}
			else {
				throw( new Error("Cannot add object of type " + typeof( item ) +
					". Must be of type " + type.toString() + "." ))
			}
		}
		
		override public function addItemAt(item:Object, index:int):void {
			if( item is type ) {
				super.addItemAt( item, index );
			}
			else {
				throw( new Error("Cannot add object of type " + typeof( item ) +
					". Must be of type " + type.toString() + "." ))
			}
		}
		
		override public function setItemAt(item:Object, index:int):Object {
			if( item is type ) {
				return( super.setItemAt( item, index ) );
			}
			else {
				throw( new Error("Cannot add object of type " + typeof( item ) +
					". Must be of type " + type.toString() + "." ))
			}
		}
		
		/**
		 * removes a given item by searching the collection for it first <br />
		 * if property is set a binarySearch will be performed on the array to
		 * find the item
		 * @param item the object to be removed
		 * @param property the property name by which to search
		 * @return true if item was removed else false
		 */
		public function removeItem( item:Object, property:String = null):Boolean
		{
			if( item is type ) {
				//return( super.setItemAt( item, index ) );
				var foundIndex:int = -1;
				if( property != null ) {
					foundIndex = this.binarySearch(property, item[property] );
				}
				else {
					foundIndex = this.getItemIndex( item );
				}
				if( foundIndex > 0 ) {
					this.removeItemAt( foundIndex );
					return true;
				}
				else {
					return false;
				}
			}
			else {
				return false;
			}
			return false;
		}
		
		/**
		 * does a binary search on this collection <br />
		 * pre: this collection is sorted by the specified property
		 * @param property specifies which property of the contained objects
		 * should be searched by
		 * @param value the value that is searched for
		 * @param start the starting index of the subsection being searched
		 * @param end the ending index of the subsectin being searched
		 */
		public function binarySearch( property:String, value:Object,
				start:int = 0, end:int = -1 ):int {
			if( this.length < 1
					|| (!this[0].hasOwnProperty(property))
					|| typeof( value ) != typeof( this[0][property] )
			) {
				return -1;
			}
			// sanatize start and end values since the user could call these as
			// well
			start = start<0?0:start;
			end = (end==-1||end>this.length-1)?this.length-1:end;
			
			var pivot:int = int((end-start)/2) + start;
			var pivotValue:Object = this[pivot][property]
			if( pivotValue == value ) {
				return pivot;
			}
			else {
				if( value < pivotValue  ) {
					if( start < pivot ) {
						return ( this.binarySearch(
								property, value, start, pivot -1 ) );
					}
					else {
						return -1;
					}
				}
				else if( value > pivotValue  ) {
					if ( end > pivot ) {
						return ( this.binarySearch(
								property, value, pivot + 1, end ) );
					}
					else {
						return -1;
					}
				}
			}
			return -1;
		}
	}
}
